*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_break.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! version 0.13.10.2  2oct2013
*! author: George G. Vega Yon


mata:
{smcl}
*! {marker parallel_break}{bf:function -{it:parallel_break}- in file -{it:parallel_break.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Stops the cluster if the mother instance has requiered so.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:parallelid}{col 20}Parallel process id. 
*!{col 6}{bf:pllinstance}{col 20}Parallel instance id.
*!{col 4}{bf:returns:}
*!{col 6}{it:Stops the cluster.}
*!{dup 78:{c -}}{asis}
void parallel_break(
    |string scalar parallelid, 
    string scalar pllinstance
    )
{
    string scalar fname, msg
    real scalar fh
    
    /* Checking empty */
    if (parallelid ==J(1,1,"")) parallelid = st_global("pll_id")
    if (pllinstance ==J(1,1,"")) pllinstance = st_global("pll_instance")
    
    /* If theres nothing to do */
    if (!strlen(parallelid+pllinstance)) return
    
    /* If the file exists: Aborting execution */
    if (fileexists(fname = sprintf("__pll%s_break", parallelid)))
    {
        /* Message */
        display(sprintf("{it:ERROR: The user has pressed -break-. Exiting}"))
        
        /* Clearing */
        stata("cap clear all")
        stata("cap clear, all")
        stata("clear")
        
        /* Opening the file and capturing the sentence */
        fh = fopen(fname, "r", 1)
        msg=fget(fh)
        fclose(fh)
        
        /* Writing the diagnosis */
        fname = sprintf("__pll%s_finito%s", parallelid, pllinstance)
        parallel_write_diagnosis(msg,fname,"User pressed break")
        
        /* Stops the execution with an error */
        _error(1)
    }
}

{smcl}
*! {marker _parallel_break}{bf:function -{it:_parallel_break}- in file -{it:parallel_break.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Stops the cluster if the mother instance has requiered so.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:parallelid}{col 20}Parallel id. 
*!{col 6}{bf:pllinstance}{col 20}Parallel instance
*!{col 4}{bf:returns:}
*!{col 6}{it:Returns -1- if the mother process has stop, else returns -0-.}
*!{dup 78:{c -}}{asis}
real scalar _parallel_break(
    |string scalar parallelid, 
    string scalar pllinstance
    )
{
    string scalar fname, msg
    real scalar fh
    
    /* Checking empty */
    if (parallelid ==J(1,1,"")) parallelid = st_global("pll_id")
    if (pllinstance ==J(1,1,"")) pllinstance = st_global("pll_instance")
    
    /* If theres nothing to do */
    if (!strlen(parallelid+pllinstance)) return(0)
    
    /* If the file exists: Aborting execution */
    if (fileexists(fname = sprintf("__pll%s_break", parallelid)))
    {        
        /* Clearing */
        stata("cap clear all")
        stata("cap clear, all")
        stata("clear")
        
        /* Opening the file and capturing the sentence */
        fh = fopen(fname, "r", 1)
        msg=fget(fh)
        fclose(fh)
        
        /* Writing the diagnosis */
        fname = sprintf("__pll%s_finito%s", parallelid, pllinstance)
        parallel_write_diagnosis(msg,fname,"User pressed break")
        
        return(1)
    }
    return(0)
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_break.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_clean.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.3 18mar2014
*! author: George G. Vega

mata:

{smcl}
*! {marker parallel_clean}{bf:function -{it:parallel_clean}- in file -{it:parallel_clean.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Removes parallel auxiliry files}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:parallelid}{col 20}Parallel of the id instance.
*!{col 6}{bf:cleanall}{col 20}Whether to remove all files no matter what parallel id.
*!{col 6}{bf:force}{col 20}Forces parallel to remove files even if sandbox is working.
*!{col 4}{bf:returns:}
*!{col 6}{it:Removes all auxiliary files.}
*!{dup 78:{c -}}{asis}
void parallel_clean(|string scalar parallelid, real scalar cleanall, real scalar force, real scalar logs) {
    
    real scalar i ;
    string colvector parallelids, sbfiles;
    
    // Checking arguments
    if (parallelid == J(1,1,"")) parallelid = st_global("LAST_PLL_ID");
    if (cleanall == J(1,1,.)) cleanall = 0;
    if (force==J(1,1,.)) force = 0;
    if (logs==J(1,1,.)) logs = 0;
    
    /* Getting the list of parallel ids that should be removed */
    if (cleanall)
    {
        parallelids = dir(pwd(),"files","__pll*") \ dir(pwd(),"files","l__pll*") \ dir(pwd(),"dirs","__pll*")

        for(i=1;i<=length(parallelids);i++)
            parallelids = regexr(regexr(parallelids,"^l?__pll",""),"_.+$","")
        parallelids = uniqrows(parallelids)
    }
    else parallelids = parallelid

    
    /* Extracting files that are in use */
    if (!force) parallel_sandbox(6,"",&sbfiles)

    for(i=1;i<=length(sbfiles);i++)
        parallelids = select(parallelids, parallelids:!=sbfiles[i])

    /* Cleaning up */
    if (length(parallelids))
    {
        for(i=1;i<=length(parallelids);i++)
        {
            parallel_recursively_rm(parallelids[i],pwd(),., logs)
            parallel_recursively_rm(parallelids[i],c("tmpdir"),., logs)
        }
    }
    else display(sprintf("{text:parallel clean:} {result: nothing to clean...}"))
    
    return
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_clean.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_divide_index.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.3 18mar2014
*! author: George G. Vega Yon


mata:
{smcl}
*! {marker parallel_compare_matrix}{bf:function -{it:parallel_compare_matrix}- in file -{it:parallel_divide_index.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Compares vectors and returns 1 if are equal.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:numvars}{col 20}Numeric matrix.
*!{col 6}{bf:trvars}{col 20}String matrix.
*!{col 6}{bf:i}{col 20}i-th row to compare.
*!{col 6}{bf:j}{col 20}j-th row to compare.
*!{col 4}{bf:returns:}
*!{col 6}{it:TRUE if all vector elements are equal, FALSE otherwise.}
*!{dup 78:{c -}}{asis}
real scalar parallel_compare_matrix(
    | real matrix numvars,
    string matrix strvars,
    real scalar i,
    real scalar j
    )
{
    real scalar numtest, strtest
    
    /* If any numvars, check if are equal */
    if (numvars != J(0,0,.)) numtest = all(numvars[i,]==numvars[j,]) 
    else numtest = 1

    /* If any strvars, check if are equal */
    if (strvars != J(0,0,"")) strtest = all(strvars[i,]==strvars[j,])
    else strtest = 1

    return((numtest & strtest))
}

{smcl}
*! {marker parallel_divide_index}{bf:function -{it:parallel_divide_index}- in file -{it:parallel_divide_index.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Generate index for grouping observations}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:numvars}{col 20}Numeric matrix.
*!{col 6}{bf:trvars}{col 20}String matrix.
*!{col 6}{bf:nclusters}{col 20}Number of clusters.
*!{col 4}{bf:returns:}
*!{col 6}{it:TRUE if all vector elements are equal, FALSE otherwise.}
*!{dup 78:{c -}}{asis}
real colvector parallel_divide_index(
    | real   matrix numvars,
    string matrix strvars,
    real scalar nclusters
)
{
    real scalar i, size, N, a, b, extra, before, after, nreps
    real colvector result
    
    if (nclusters == J(1,1,.)) nclusters = strtoreal(st_global("PLL_CLUSTERS"))
    
    /* Defining variables */
    if (numvars == J(0,0,.) & strvars == J(0,0,""))
        N = c("N")
    else if (numvars != J(0,0,.) & strvars == J(0,0,""))
        N = rows(numvars)
    else if (numvars == J(0,0,.) & strvars != J(0,0,""))
        N = rows(strvars)

    size   = J(1,1,floor(N/nclusters))
    result = J(N,1,0)
    
    /* Assigning blocks */
    if (numvars == J(0,0,.) & strvars == J(0,0,""))
    {
        /* Clean assigment */
        for(i=1;i<=nclusters;i++)
        {
            a = (i-1)*size + 1
            b = min((i*size, N))

            if (i==nclusters) result[a::N] = J(length(a::N),1,i)
            else result[a::b] = J(length(a::b),1,i)
        }
    }
    else 
    {
        /* Checking by over -numvars- */
        a = 0; b = 0
        extra  = 0
        for(i=1;i<=nclusters;i++)
        {
            a = (i-1)*size + 1 + extra
            
            /* If, from the last process, the ending is */
            if (b > a) a = b + 1
            
            b = min((i*size, N))

            /* If overlies */
            if (a > b) b = a + floor((N - a)/(nclusters - i + 1))
            
            /* If it is the last observation */
            if (i==nclusters | b>=N)
            {
                result[a::N] = J(length(a::N),1,i)
                break
            }
            else result[a::b] = J(length(a::b),1,i)
            
            /* Everything Ok? */
            before = 0
            after  = 0
            nreps  = 0
            while(parallel_compare_matrix(numvars,strvars,b,b+1))
            {
                /* Go back */
                if (a < (b + before - 1) & i < nclusters) --before
                
                if (N > (b + after + 1)) ++after
                
                if (++nreps > N) {
                    errprintf("Insufficient number of groups:\nCan not divide the dataset into -%g- clusters.\n", nclusters)
                    exit(198)
                }

                /* Checking before */
                //if (numvars[b + before,.] != numvars[b + before + 1,.])
                if(!parallel_compare_matrix(numvars,strvars,b+before,b+before+1))
                {
                    /* Moving the upper bound */
                    b = b + before
                    
                    /* Fixing next starting point */
                    extra = before
                    break
                }
                /* Checking after */
                //if (numvars[b + after ,.] != numvars[b + after + 1,.])
                if(!parallel_compare_matrix(numvars,strvars,b+after,b+after+1))
                {
                    extra = after
                    a = b
                    b = min((b + after,N))
                    result[a::b] = J(length(a::b),1,i)
                    break
                }
            }
            
            /* If no change, extra moves to 0 */
            if (before == 0 & after == 0) extra = 0

        }
    }
    
    /* Correcting biases */

    return(result)
}

end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_divide_index.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_eststore.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! parallel_estout vers 0.14 10may2014
*! auth George G Vega
mata
// mata clear
/*
 * @brief Process e() and store it as a plain-text file
 * @param stmatname Name of e()
 * @param fn File name where to save the file
 * @param fappend whether to append or replace the file
 */
{smcl}
*! {marker parallel_eststore}{bf:function -{it:parallel_eststore}- in file -{it:parallel_eststore.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}{asis}
void function parallel_eststore(
    | string scalar fn,
    string scalar stlist,
    real scalar fappend
)
{
    // Variables definition
    real scalar fh, fh0
    real scalar i, j, ncol, nrow, nhead, nnewcols
    real matrix stmat, stmattmp
    string rowvector fheader, tabheader
    string matrix stcolnames0, strownames0
    string rowvector stcolnames, strownames
    real rowvector namesorder
    string scalar txt, tabs, fn0, randtype
    
    string scalar stmatname
    
    if (fappend   == J(1,1,.)) fappend = 1
    if (stlist == J(1,1,"")) stlist = "b"
    
    stmatname = "e("+stlist+")"
    
    // Matrix parsing
    stmat       = st_matrix(stmatname)
    stcolnames0 = ("","N")\st_matrixcolstripe(stmatname)
    strownames0 = ("","_"+stlist) // st_matrixrowstripe(stmatname)
    
    stmat = J(rows(stmat),1,st_numscalar("e(N)")), stmat

    // Col and row names
    stcolnames = J(1, rows(stcolnames0),"")
    for(i=1;i<=rows(stcolnames0);i++)
        for(j=1;j<=cols(stcolnames0);j++)
            stcolnames[i] = stcolnames[i]+stcolnames0[i,j]

    strownames = J(1, rows(strownames0),"")
    for(i=1;i<=rows(strownames0);i++)
        for(j=1;j<=cols(strownames0);j++)
            strownames[i] = strownames[i]+strownames0[i,j]
            
    // Merging names
    ncol = length(stcolnames)
    nrow = length(strownames)

    tabheader = J(1,nrow*ncol,"")
    
    for(i=1;i<=nrow;i++)
        for(j=1;j<=ncol;j++)
            tabheader[j + ncol*(i-1)] = strownames[i]+"_"+stcolnames[j]

    // File parsing
    if (fn == J(1,1,"")) fn = sprintf("__pll%s_eststore%04.0f.tab",st_local("pll_id"),strtoreal(st_local("pll_instance")))
    if (!fappend) unlink(fn)
    
    
    // If no such file, creat a new one
    if (!fileexists(fn))
    {
        fh = _fopen(fn, "w")
        if (fh<0) _error(1)
        
        // Writing the file header
        nhead = length(tabheader)
        txt = ""
        for(i=1;i<=nhead;i++) 
            txt = txt + tabheader[i] + 
                (i == nhead ? "" : sprintf("\t"))
        fput(fh, txt)
        
        // Writing the file lines
        for(i=1;i<=nrow;i++)
        {
            txt = ""
            for(j=1;j<=ncol;j++)
                txt = txt + sprintf("%g",stmat[i,j]) + 
                    (j==ncol? "" : sprintf("\t"))
            fput(fh, txt)
        }
                
        fclose(fh)
        
        return
    }
    else 
    {
    
        fh = _fopen(fn,"r")
        
        // Getting the order of the variables (so that we can write)
        fheader = tokens(fget(fh),sprintf("\t"))
        fclose(fh)
        fheader = select(fheader,fheader:!=sprintf("\t"))
        nhead = length(fheader)
        
        namesorder = J(1,ncol*nrow,.)
        nnewcols   = 0

        // Looking for the same varname
        for(j=1;j<=ncol*nrow;j++)
        {
            for(i=1;i<=nhead;i++)
            {
                if (fheader[i] == tabheader[j])
                {
                    namesorder[j] = i
                    break
                }
            }
            
            if (namesorder[j] == .) 
                namesorder[j] = nhead + (++nnewcols)
        }
        
        // Ordering the variables accordingly to the file
        stmattmp = J(nrow,max((max(namesorder),nhead)),.)
        for(j=1;j<=ncol;j++)
            stmattmp[,namesorder[j]] = stmat[,j]

        // In the case of new columns, the rest of the rows must be modified
        if (nnewcols)
        {
            
            fh    = fopen(fn, "r")
            fh0   = fopen(
                (fn0=parallel_randomid(10,randtype,1,1,1)), 
                "w")
            
            txt   = fget(fh)
            txt
            for(i=1;i<=length(namesorder);i++)
                if (namesorder[i]>nhead) 
                    txt = txt + sprintf("\t")+tabheader[i]
            txt
            fput(fh0,txt)
            
            tabs = ""
            for(i=1;i<=nnewcols;i++) tabs = tabs+sprintf("\t")
            
            // Adding the new lines
            while((txt=fget(fh))!=J(0,0,"") )
                fput(fh0, txt+tabs)

            // Renaming the file
            fclose(fh)
            fclose(fh0)
            stata("copy "+fn0+" "+fn+", replace")
            unlink(fn0)
        }
        
        // Writing the file lines
        fh = fopen(fn, "a")
        
        ncol = cols(stmattmp)
        
        for(i=1;i<=nrow;i++)
        {
            txt = ""
            for(j=1;j<=ncol;j++)
                txt = txt + sprintf("%g",
                    stmattmp[i,j]) + 
                    sprintf((j==ncol? "" : "\t")
                )
                
            fput(fh, txt)
        }
        
        // Finishing
        fclose(fh)        
        return
    }
}

/*
 * @brief List returning objects (scalar/macro/matrix/function)
 * @param typeofr Return type, could be e, r or s.
 */
{smcl}
*! {marker parallel_xreturnlist}{bf:function -{it:parallel_xreturnlist}- in file -{it:parallel_eststore.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}{asis}
string colvector function parallel_xreturnlist(|string scalar typeofr)
{

    if (!args()) typeofr = "e"

    if (!regexm(typeofr,"^(e|r|s)$")) _error(1)

    string colvector out
    out = J(0,1,"")
    
    if (typeofr == "e") /* ereturn type */
    {
        stata("local x : e(scalars)")
        out = st_local("x")
        
        stata("local x : e(macros)")
        out = out\st_local("x")
        
        stata("local x : e(matrices)")
        out = out\st_local("x")
        
        stata("local x : e(functions)")
        out = out\st_local("x")
    }
    else if (typeofr == "r") /*return type*/
    {
        stata("local x : r(scalars)")
        out = st_local("x")
        
        stata("local x : r(macros)")
        out = out\st_local("x")
        
        stata("local x : r(matrices)")
        out = out\st_local("x")
        
        stata("local x : r(functions)")
        out = out\st_local("x")
    }
    else if (typeofr == "s") /*sreturn type*/
    {
        stata("local x : s(macros)")
        out = st_local("x")
    }
    
    return(out)
}

// General manager of parallel_estout
// Possible actions:
//  0: Start
//  1: Merge
{smcl}
*! {marker parallel_eststore_start}{bf:function -{it:parallel_eststore_start}- in file -{it:parallel_eststore.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}{asis}
void function parallel_eststore_start(|string scalar fn)
{
    // Checking if the file exists
    if (args() == 0)
        fn = sprintf("__pll%s_estout%04.0f.tab",st_local("pll_id"),strtoreal(st_local("pll_instance")))
    unlink(fn)    
}

/*
 * @brief Merges parallel_estout_save() files
 * @param fn Name of the output file
 * @param fns List of file names
 * @param expr Expresion to expand in the form of "%fmts, numlist"
 */
{smcl}
*! {marker parallel_eststore_append}{bf:function -{it:parallel_eststore_append}- in file -{it:parallel_eststore.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}{asis}
void parallel_eststore_append(
    string scalar fn,
    | string scalar fns,
    string scalar expr)
{

    real scalar i, nclusters
    string scalar parallelid
    string rowvector files
    
    /* If there are no arguments, ther it should be parallel using it */    
    if (args()==1) 
    {
        /* Retrieving information from parallel */
        parallelid = st_global("r(pll_id)")
        nclusters  = strtoreal(st_global("PLL_CLUSTERS"))
        
        files = J(1,nclusters,"")
        for(i=1;i<=nclusters;i++)
            files[i] = sprintf("__pll%s_eststore%04.0f.tab", parallelid, i)
    }
    else if (args() == 2) files = tokens(fns)
    else files = tokens(parallel_expand_expr(expr))
    
    if (parallelid == J(1,1,"")) parallelid = parallel_randomid(10,"",1,1,1)
    
    /* Checking which files exists */
    for(i=1;i<=length(files);i++)
    {
        if (!fileexists(files[i])) files[i] = ""
        files = select(files, files :!= "")
    }
    
    /* If no file, then exit */
    if (!length(files)) return
    
    /* Preserving information and appending the dataset */
    real scalar N
    N = c("N")
    if (N) stata("qui save __pll"+parallelid+"estout_preserve.dta, replace")
    for(i=1;i<=length(files);i++)
    {
        stata(sprintf("qui insheet using %s, clear", files[i]))
        unlink(files[i])
        if (i!=1) stata(sprintf("qui append using %s", fn))
        stata(sprintf("qui save %s, replace", fn))
    }
    
    /* Saving and compressing */
    stata("qui compress")
    stata(sprintf("save %s, replace", fn))
    
    display(sprintf("The file -%s- has been created at:{break}{tab}%s",fn,pwd()))
    
    if (N)
    {
        stata("qui use __pll"+parallelid+"estout_preserve.dta, clear")
        unlink("__pll"+parallelid+"estout_preserve.dta")
    }
    
    return
}

end

/*

local pll_instance 1
local pll_id 1asd156
local reps 100

timer clear
forval i=1/20 {
    sysuse auto, clear
    timer on 1
    qui bs, reps(`reps') : regress mpg weight c.weight#c.weight foreign
    timer off 1

    tempfile x
    save `x', replace

    timer on 2
    mata parallel_eststore_start("__pllest`i'.tab")
    forval j=1/`reps' {
        qui use `x', clear
        bsample
        qui regress mpg weight c.weight#c.weight foreign
        mata parallel_eststore("__pllest`i'.tab")
    }

    insheet using "__pllest`i'.tab", tab names clear
    qui summ
    timer off 2
}
timer list

m parallel_eststore_append("__pllest.dta","","__pllest%g.tab,1/20")

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_eststore.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_expand_expr.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.4 9apr2014
*! auth George G. Vega

 * @param 

mata:
{smcl}
*! {marker parallel_expand_expr}{bf:function -{it:parallel_expand_expr}- in file -{it:parallel_expand_expr.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Expands a fmt string combining numlists}
*!{col 4}{bf:author(s):}
*!{col 6}{it:George G. Vega}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:expr}{col 20}An expression containing a fmt and numlists
*!{col 6}{bf:pchar}{col 20}(optional) Parsing char which separates -fmt- and numlists
*!{col 4}{bf:returns:}
*!{col 6}{it:}
*!{dup 78:{c -}}{asis}
string scalar function parallel_expand_expr(
    string scalar expr,
    |string scalar pchar,
    string scalar sep
    )
{
    /* Variables definition */
    string colvector expexpr
    string scalar bexpr, out
    real scalar nexpr, i
        
    if (args()<2 | pchar==J(1,1,"")) pchar = ","
    if (args()<3) sep = " "
    
    /* Parsing the expression */
    expexpr = tokens(expr,pchar)
    expexpr = select(expexpr, expexpr :!= ",")
    if ((nexpr=length(expexpr))==1) _error(1)
        
    bexpr = expexpr[1]
    expexpr = expexpr[2..nexpr]
    nexpr = nexpr-1
    
    /* Getting the expresion */
    pointer(string rowvector) colvector numlists
    numlists = J(nexpr, 1, NULL)
    
    for(i=1;i<=nexpr;i++) 
    {
        stata(`"numlist ""'+expexpr[i]+`"""')
        numlists[i] = &tokens(st_global("r(numlist)"))
        
    }
    
    /* Creating the expression */
    real scalar i1, i2, i3, i4
    string rowvector l1, l2, l3, l4

    out = ""
    if (nexpr == 1)
    {
        l1 = *numlists[1]
        for(i1=1;i1<=length(l1);i1++)
            out = out +sep+ sprintf(bexpr,strtoreal(l1[i1]))
    }
    else if (nexpr == 2)
    {
    
        l1 = *numlists[1]
        l2 = *numlists[2]
        for(i1=1;i1<=length(l1);i1++)
            for(i2=1;i2<=length(l2);i2++)
                out = out +sep+ sprintf(bexpr,strtoreal(l1[i1]),strtoreal(l2[i2]))
    }
    else if (nexpr == 3)
    {
    
        l1 = *numlists[1]
        l2 = *numlists[2]
        l3 = *numlists[3]
        for(i1=1;i1<=length(l1);i1++)
            for(i2=1;i2<=length(l2);i2++)
                for(i3=1;i3<=length(l3);i3++)
                    out = out +sep+ sprintf(bexpr,strtoreal(l1[i1]),strtoreal(l2[i2]),strtoreal(l3[i3]))
    }
    else if (nexpr == 4)
    {
    
        l1 = *numlists[1]
        l2 = *numlists[2]
        l3 = *numlists[3]
        l4 = *numlists[4]
        for(i1=1;i1<=length(l1);i1++)
            for(i2=1;i2<=length(l2);i2++)
                for(i3=1;i3<=length(l3);i3++)
                    for(i4=1;i4<=length(l4);i4++)
                        out = out +sep+ sprintf(bexpr,strtoreal(l1[i1]),strtoreal(l2[i2]),strtoreal(l3[i3]),strtoreal(l4[i4]))
    }

    return(out)
}
end
/*
m parallel_expand_expr("%02.0f_%02.0f/mcci.dta, 2007/2013,1/12")
m parallel_expand_expr("%02.0f_%02.0f/%02.0f, 2007/2013,1/12,0/1")
m parallel_expand_expr("%02.0f_%02.0f/%02.0f, 2007/2013,1(4)12,0/1")
m parallel_expand_expr("%02.0fa%02.0fb%02.0fc%02.0fd, 1/2,3/4,5/6,7/8")
*/

/* Crear una lista de la forma 2013_01 2013_02 ... 2015_12 */
// m parallel_expand_expr("%04.0f_%02.0f, 2013/2015, 1/12")

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_expand_expr.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_export_globals.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! parallel_export_globals vers 0.14.7.23 23jul2014 @ 22:10:27
*! author: George G. Vega Yon

mata:

{smcl}
*! {marker parallel_export_globals}{bf:function -{it:parallel_export_globals}- in file -{it:parallel_export_globals.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Looks for global macros and writes to the dofile.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:outname}{col 20}Name of the new do-file.
*!{col 6}{bf:out_fh}{col 20}If a file is already open, the user can export the globals to it.
*!{col 4}{bf:returns:}
*!{col 6}{it:A do-file eady to be runned and define globals.}
*!{dup 78:{c -}}{asis}
void parallel_export_globals(|string scalar outname, real scalar ou_fh) {
    
    real   scalar isnewfile, glob_ind
    string scalar macname, macvalu, FORBIDDEN, line
    string colvector global_names

    if (outname == J(1,1,"")) outname = parallel_randomid(10,"",1,1,1)
    
    if (ou_fh == J(1,1,.)) {
        if (fileexists(outname)) unlink(outname)
        ou_fh = fopen(outname, "w", 1)
        isnewfile = 1
    }
    else isnewfile = 0

    // Step 1
    FORBIDDEN = "^(S\_FNDATE|S\_FN|F[0-9]|S\_level|S\_ADO|S\_FLAVOR|S\_OS|S\_MACH|!)"

    global_names = st_dir("global", "macro", "*")
    for(glob_ind=1; glob_ind<=rows(global_names); glob_ind++) {
        /* Only pic globals with a-zA-Z names */
        if (!regexm(global_names[glob_ind,1], "^[a-zA-Z]")) continue

        macname = global_names[glob_ind,1]
        if (!regexm(macname, FORBIDDEN)){
            macvalu = st_global(macname)
            line = "global "+macname+" "+macvalu
            fput(ou_fh, line)
        }
    }
    
    if (isnewfile) fclose(ou_fh)
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_export_globals.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_export_programs.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.4 16apr2014
*! author: George G. Vega

mata:
{smcl}
*! {marker parallel_export_programs}{bf:function -{it:parallel_export_programs}- in file -{it:parallel_export_programs.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:export programs loaded in the current sesion.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:ouname}{col 20}Name of the file that will contain the programs.
*!{col 6}{bf:programlist}{col 20}List of programs to be exported.
*!{col 6}{bf:inname}{col 20}Name of the tmp file that will be used as log.
*!{col 4}{bf:return:}
*!{col 6}{it:A do-file ready to be runned to load programs.}
*!{dup 78:{c -}}{asis}
real scalar parallel_export_programs(
    string scalar ouname ,
    |string scalar programlist,
    string scalar inname
    ) 
    {
    
    real scalar in_fh, ou_fh
    string scalar line, oldsettrace
    string scalar pathead, patnext
    
    if (programlist==J(1,1,"")) programlist = "_all"
    if (inname==J(1,1,"")) inname = parallel_randomid(10,"",1,1,1)
    
    // Writing log
    oldsettrace =c("trace")
    if (oldsettrace == "on") stata("set trace off")
    stata("qui log using "+inname+", text replace name(plllog"+st_local("parallelid")+")")
    display(sprintf("{hline 80}{break}{result:Exporting the following program(s): %s}",programlist))
    stata("capture noisily program list "+programlist)
    stata("local err = _rc")

    real scalar err
    if ( (err = strtoreal(st_local("err"))) ) {
        stata("qui log close plllog"+st_local("parallelid"))
        stata("set trace "+oldsettrace)    
        return(err);
    }

    stata("qui log close plllog"+st_local("parallelid"))
    stata("set trace "+oldsettrace)
    
    // Opening files
    in_fh =_fopen(inname, "r")
    ou_fh =_fopen(ouname, "rw")
    
    // If any error occurs
    if (ou_fh < 0) {
        fclose(in_fh)
        return
    }
    
    fwrite(ou_fh,sprintf("\n"))
    
    // REGEX Patterns
    string scalar space
    space = "[\s ]*"+sprintf("\t")+"*"
    pathead = "^"+"[^0-9][a-zA-Z_]+([a-zA-Z0-9_]*)(,"+space+"[a-zA-Z]*)?[:]"+space+"$"
    patnext = "^[>] "
    
    while ((line = fget(in_fh))!=J(0,0,"")) {
        
        // Enters if it is a start of a program
        if(regexm(line, pathead)) {
            // Writes the header
            fput(ou_fh, sprintf("program def %s", subinstr(line, ":", "")))
            line = fget(in_fh)
        
            // While it is whithin the program
            while (line!=J(0,0,"")) {
                 // If it is a trimmed version of the program
                if (regexm(line, patnext)) {
                    fwrite(ou_fh, regexr(line, patnext,""))
                }
                // If it is the last line of the program
                else if (strlen(line) == 0) {
                    fput(ou_fh, sprintf("\nend"))
                    break
                }
                else { // If it is ok
                    line = regexr(line, "^[\s ]*[0-9]+\.", "")
                    fwrite(ou_fh, strltrim(sprintf("\n%s",line)))
                }
                line = fget(in_fh)
            }
        }
    }
    
    // Cleaning the files
    fclose(in_fh)
    unlink(inname)
    fwrite(ou_fh,sprintf("\nend\n"))
    fclose(ou_fh)
    display("{hline 80}")
    return(0)
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_export_programs.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_finito.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! version 0.14.7.22  22jul2014
*! author: George G. Vega Yon

mata:
{smcl}
*! {marker parallel_finito}{bf:function -{it:parallel_finito}- in file -{it:parallel_finito.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Waits until every process finishes or stops the processes}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:parallelid}{col 20}Parallel instance id.
*!{col 6}{bf:nclusters}{col 20}Number of clusters.
*!{col 6}{bf:timeout}{col 20}Time (in secs) before abort.
*!{col 4}{bf:returns:}
*!{col 6}{it:Number of clusters that stopped with error.}
*!{dup 78:{c -}}{asis}
real scalar parallel_finito(
    string scalar parallelid,
    | real scalar nclusters,
    real scalar timeout
    )
    {
    
    display(sprintf("{it:Waiting for the clusters to finish...}"))
    
    // Setting default parameters
    if (nclusters == J(1,1,.)) nclusters = strtoreal(st_global("PLL_CLUSTERS"))
    if (timeout == J(1,1,.)) timeout = 6000
    
    // Variable definitios
    real scalar in_fh, out_fh, time
    real scalar suberrors, i, errornum, retcode
    string scalar fname
    string scalar msg
    real scalar bk, pressed
    real rowvector pendingcl
    
    // Initial number of errors
    suberrors = 0
    
    /* Temporaly sets break key off */
    /* In windows (by now) parallel cannot use the breakkey */
    bk=querybreakintr();
    if (c("os")!="Windows") 
    {
        bk = setbreakintr(0)
        pressed=0
    }
    
    /* Checking conextion timeout */
    pendingcl = J(1,0,.)
    for(i=1;i<=nclusters;i++)
    {        
        /* Building filename */
        fname = sprintf("__pll%s_do%04.0f.log", parallelid, i)
        time = 0
        while (!fileexists(fname) & ((++time)*100 < timeout) & !breakkey())
            stata("sleep 100")
            
        if (!fileexists(fname))
        {
            display(sprintf("{it:cluster %04.0f} {text:has finished with a connection error -601- (timeout) ({stata search r(601):see more})...}", i))
            suberrors++
            continue
        }
        else pendingcl = pendingcl, i
            
        timeout = timeout - time*100
    }
    
    /* If there are as many errors as clusters, then exit */
    if (suberrors == nclusters) return(suberrors)
    
    string scalar logfilename, tmpdirname

    while(length(pendingcl)>0)
    {
        
        // Building filename
        for (i=1;i<=nclusters;i++)
        {
            /* If this cluster is ready, then continue */
            if (!any(pendingcl :== i)) continue
            
            fname = sprintf("__pll%s_finito%04.0f", parallelid, i)
            
            if (breakkey() & !pressed) 
            { /* If the user pressed -break-, each instance will try to finish the work through parallel finito */
                /* Message */
                display(sprintf("{it:The user pressed -break-. Trying to stop the clusters...}"))
            
                /* Openning and checking for the new file */
                fname = sprintf("__pll%s_break", parallelid)
                if (fileexists(fname)) _unlink(fname)
                out_fh = fopen(fname, "w", 1)
                
                /* Writing and exit */
                fput(out_fh, "1")
                fclose(out_fh)
                
                pressed = 1
                fname = sprintf("__pll%s_finito%04.0f", parallelid, i)
                
            }
        
            if (fileexists(fname)) // If the file exists
            {
                /* Opening the file and looking for somethign different of 0
                (which is clear) */

                /* Copying log file */
                logfilename = sprintf("%s__pll%s_do%04.0f.log", (regexm(c("tmpdir"),"(/|\\)$") ? "" : "/"), parallelid, i)
                stata(sprintf(`"cap copy __pll%s_do%04.0f.log "%s%s", replace"', parallelid, i, c("tmpdir"),logfilename))
                retcode = _unlink(pwd()+logfilename)
                /* Sometimes Stata hasn't released the file yet. Either way, don't error out  */
                if (retcode !=0){
                    stata("sleep 2000")
                    _unlink(pwd()+logfilename)
                }

                in_fh = fopen(fname, "r", 1)
                if ((errornum=strtoreal(fget(in_fh))))
                {
                    msg = fget(in_fh)
                    if (msg == J(0,0,"")) display(sprintf(`"{it:cluster %04.0f} {text:Exited with error -%g- ({stata parallel viewlog %g, e(%s):view log})...}"', i, errornum, i, parallelid))
                    else display(sprintf(`"{it:cluster %04.0f} {text:Exited with error -%g- %s ({stata parallel viewlog %g, e(%s):view log})...}"', i, errornum, msg, i, parallelid))
                    suberrors++
                }
                else display(sprintf("{it:cluster %04.0f} {text:has exited without error...}", i))
                fclose(in_fh)

                /* Checking tmpdir */
                tmpdirname = sprintf("%s"+ (regexm(c("tmpdir"),"(/|\\)$") ? "" : "/") + "__pll%s_tmpdir%04.0f", c("tmpdir"),parallelid,i)
                parallel_recursively_rm(parallelid,tmpdirname,1)
                rmdir(tmpdirname)
                
                /* Taking the finished cluster out of the list */
                pendingcl = select(pendingcl, pendingcl :!= i)
                
                continue
            } /* Else just wait for it 1/10 of a second! */
            else stata("sleep 100")
        }
    }
    
    /* Returing to old break value */
    if (querybreakintr()!=bk) 
    {
        breakkeyreset()
        (void) setbreakintr(bk)
    }
    
    real scalar linesize
    linesize = c("linesize") > 80 ? 80 : c("linesize")
    display(sprintf("{hline %g}{break}{text:Enter -{stata parallel printlog 1, e(%s):parallel printlog #}- to checkout logfiles.}{break}{hline %g}", linesize, parallelid, linesize))
    
    return(suberrors)
    
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_finito.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_for.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
cap mata: mata drop parallel_for()
mata:
{smcl}
*! {marker parallel_for}{bf:function -{it:parallel_for}- in file -{it:parallel_for.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}{asis}
void parallel_for(real matrix data, string scalar fun, | real scalar byrow) {
    
    real scalar i, obsleft, clsize, nobs
    real rowvector sizes
    
    // Setting how many obs should be
    if (byrow == J(1,1,.)) byrow = 1
    
    if (byrow) nobs = rows(data)
    else nobs = cols(data)
    
    clsize  = round(nobs/4)
    obsleft = nobs
    sizes = J(1,0,.)
    while ((obsleft = (obsleft - clsize)) > 0) {
        sizes = sizes, clsize
    }
    
    if ((obsleft = nobs - sum(sizes)) > 0) sizes = sizes, obsleft
    
    fun = sprintf("mata:\nfor(i=1;i<=%g;i++) {\n\t%s\n}\nend",rows(data), fun)
    parallel_write_do(fun, "123123", 4)
}
end

parallel clean, all
mata: parallel_for(J(50,2,1),"sum[1..i]")
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_for.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_montecarlo.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
mata

// Resampling algorithm
{smcl}
*! {marker parallel_resample}{bf:function -{it:parallel_resample}- in file -{it:parallel_montecarlo.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}{asis}
real colvector function parallel_resample(
        | real scalar size,
        real colvector weights
        )
{
        real colvector permut, index, newsample
        real scalar N0, N1, n, i, k, j

        N0 = c("N")

        // Getting the sample size
        if (size < 1) n = max( (1,round(size*N0)) )
        else {
        if (N0 < size)
        {
            printf("N is smaller than the requiered size\n")
            n = N0
        }
        else n = size
    }

        // Expanding the observations index (or not!)
    k = 0
        if (weights != J(0,1,.))
        {
        N1 = sum(weights)
                index = J(sum(weights),1,1)
                for(i=1;i<=N0;i++)
                {
                        j=0
                        while(j++ < weights[i])
                                index[++k] = i
                }
        }
        else
    {
        N1 = N0
        index = 1::N0
    }

    index = index\index

    // Getting the selected obs id
        permut = index[order(runiform(N1*2,1),1)[1::n]]

    // Creating the weights
    newsample = J(N0,1,0)
    for(i=1;i<=n;i++)
        newsample[permut[i]] = newsample[permut[i]] + 1
        

    return(newsample)

}

end
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_montecarlo.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_normalizepath.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.4 18apr2014
*! author: George G. Vega Yon


mata:
{smcl}
*! {marker parallel_normalizepath}{bf:function -{it:parallel_normalizepath}- in file -{it:parallel_normalizepath.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Path parsing.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:Path}{col 20}to be analized.
*!{col 6}{bf:Whether}{col 20}to export the results as local or not.
*!{col 4}{bf:return:}
*!{col 6}{it:A vector with path\fullpath\fileext\filename. }
*!{dup 78:{c -}}{asis}
transmorphic function parallel_normalizepath(
    string scalar path,
    | real scalar exportaslocal
    )
    {
    
    string scalar filename, fileext, fullpath, filedir, curpath
    string rowvector parts
    real scalar i, isfile
    
    if (exportaslocal == J(1,1,.)) exportaslocal = 0
        
    // Replacing folder sep
    fullpath = subinstr(path, "\", "/")
    fullpath = subinstr(path, `"""', "")
    
    // Verifying if there is anything
    if (fileexists(fullpath)) isfile = 1
    else if (direxists(fullpath)) isfile = 0
    else _error(601)
    
    curpath = regexr(pwd(), "/$", "")
    
    if (isfile) {
        if(fileexists(pwd()+fullpath))
            fullpath = subinstr(pwd(),"\","/")+fullpath
    }
    else {
        if(direxists(pwd()+fullpath))
            fullpath = subinstr(pwd(),"\","/")+fullpath
    }
    
    // Cleaning ".." and spliting (parsing)
    parts = tokens(fullpath,"/")
    if (cols(parts) >= 3) {
        for(i=3;i<=cols(parts);i++) {
            if (parts[i] == "..") parts[(i-2)..i] = J(1,3,"")
        }
    }
    
    // Merging all
    fullpath = ""
    for(i=1;i<=cols(parts);i++) fullpath = fullpath+parts[i]
    
    // Replacing "//"
    while (strlen(fullpath) != strlen(regexr(fullpath, "//", "/")))
        fullpath = regexr(fullpath, "//", "/")
    
    // Last check
    if (isfile) {
        if(!fileexists(fullpath)) _error(1)
    }
    else {
        if(!direxists(fullpath)) _error(1)
    }
    
    // Extracting details
    pathsplit(fullpath, filedir, filename)
    fileext = pathsuffix(filename)
    
    /* Checking last-bar */
    if (!regexm(filedir,"(\/|\\)$")) filedir = filedir+"/"
        
    if (exportaslocal) {
        st_local("filedir",filedir)
        st_local("filename",filename)
        st_local("fullpath",fullpath)
        st_local("fileext",fileext)
    }
    else  return((path\fullpath\fileext\filename))
    
}
end
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_normalizepath.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_randomid.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 1.14.5 06may2014
*! author: George G. Vega Yon

mata:
{smcl}
*! {marker parallel_randomid}{bf:function -{it:parallel_randomid}- in file -{it:parallel_randomid.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Mata's Random id generation.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:n}{col 20}Number of random ids to generate.
*!{col 6}{bf:randtype}{col 20}Type of random algorithm to use.
*!{col 6}{bf:alpha}{col 20}Whether to use or not alphanum.
*!{col 6}{bf:nele}{col 20}Length of each random id generated.
*!{col 6}{bf:ilent}{col 20}Whether to run quietly or not.
*!{col 4}{bf:returns:}
*!{col 6}{it:String colvector of random ids.}
*!{dup 78:{c -}}{asis}
string colvector parallel_randomid(|real scalar n, string scalar randtype, real scalar alpha, real scalar nele, real scalar silent) {
    
    string scalar curseed
    string scalar newseed, tmpid
    string scalar line
    string vector id, id2
    real scalar rn_fh, i, j
    
    id = J(0,1,"")
    
    if (alpha == J(1,1,"")) alpha = 1
    if (nele == J(1,1,.)) nele = 1
    if (silent == J(1,1,.)) silent = 0
    
    // Checking if randtype is supported
    if (!regexm(randtype,"^(current|random.org|datetime)$") & strlen(randtype) > 0) {
        errprintf("randtype -%s- not supported\nPlease try with -random.org- or -datetime-\n", randtype)
        exit(198)
    }
    
    // Parsing id length
    if (n==J(1,1,.)) n = 10
    
    // Keeping the current seed value (if its going to change)
    if (randtype!=J(1,1,"")) curseed = c("seed")

    /* Checking whether PLL has already used an ID */
    if (!strlen(st_global("PLL_LASTRNG"))) st_global("PLL_LASTRNG","0")
    
    real scalar PLL_LASTRNG
    PLL_LASTRNG = strtoreal(st_global("PLL_LASTRNG")) + 1
    st_global("PLL_LASTRNG", strofreal(PLL_LASTRNG))

    n = n - strlen(strofreal(PLL_LASTRNG))
    
    if (randtype=="random.org") {
        if (!silent) printf("Connecting to random.org API...")
        if (alpha) { /* Gets strings */
            rn_fh = _fopen("http://www.random.org/strings/?num="+strofreal(nele)+"&len="+strofreal(n)+"&digits=on&upperalpha=off&loweralpha=on&unique=on&format=plain&rnd=new","r")
        }
        else {       /* Gets integers */
            rn_fh = _fopen("http://www.random.org/integers/?num="+strofreal(nele)+"&min="+ strofreal(10^(n))+"&max="+strofreal(10^(n+1)-1)+"&col=1&base=10&format=plain&rnd=new", "r")
        }
        
        if (rn_fh >= 0) { // If the connection works fine
            id = J(0,1,"")
            while ((line=fget(rn_fh)) != J(0,0,"") ) {
                id = id\ line+strofreal(PLL_LASTRNG)
            }
            fclose(rn_fh)
            if (!silent) printf("success!\n")
            
            // Returns the random id from random.org
            for(i=1;i<=nele;i++) {
                if (!silent) display(sprintf("Your random id is {ul:%s} (saved in {stata return list:r(id"+strofreal(i)+")})\n", id[i]))
                st_global("r(id"+strofreal(i)+")", id[i])
            }
            return(id)
            
        }
        else { // If the connection does not work
            errprintf("Can not connect to -random.org-\n")
            exit(rn_fh)
        }
    }
    else if (randtype=="datetime") {
        newseed = strtrim(sprintf("%15.0f",
            sum(ascii(c("current_date")))+ /* Date component */
            sum(ascii(c("user")))+         /* Usr name component */
            strtoreal(strreverse(subinstr(c("current_time"),":","")))) /* Time component */
            )
        stata("set seed "+newseed)
    }
    
    if (alpha) id2 = (tokens(c("alpha")), strofreal(1..9),tokens(c("alpha")), strofreal(1..9)    )
    else id2 = strofreal(1..9),strofreal(1..9),strofreal(1..9),strofreal(1..9)

    for(j=1;j<=nele;j++) {
        id2 = jumble(id2')'
        tmpid = ""        
        for(i=1;i<=n;i++) {
            tmpid = tmpid+id2[i]
        }
        id = id\tmpid+strofreal(PLL_LASTRNG)
    }
    
    if (randtype!=J(1,1,"")) stata("set seed "+curseed)
    
    // Returns the random id
    for(i=1;i<=nele;i++) {
        if (!silent) display(sprintf("Your random id is {ul:%s} (saved in {stata return list:r(id"+strofreal(i)+")})\n", id[i]))
        st_global("r(id"+strofreal(i)+")", id[i])
    }
    
    return(id)
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_randomid.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_recursively_rm.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}

mata

/*
 *@brief Recursively removes parallel tmpfiles
 *@param parallelid Id of the parallel process to clean
 *@param path Path where to search for auxiliary files
 */
{smcl}
*! {marker parallel_recursively_rm}{bf:function -{it:parallel_recursively_rm}- in file -{it:parallel_recursively_rm.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}{asis}
void function parallel_recursively_rm(string scalar parallelid ,| string scalar path, real scalar atomic, real scalar rmlogs)
{
    if (path==J(1,1,"")) path = pwd()
    else if (!regexm(path,"[/\]$")) path = path+"/"

    // display("{hline}{break}Entering folder "+path)
    
    if (atomic == J(1,1,.)) atomic = 0
    if (rmlogs == J(1,1,.)) rmlogs = 0
    
    string scalar pattern
    if (!atomic) pattern = "__pll"+parallelid+"_*"
    else pattern = "*"

    string colvector dirs
    string colvector files

    /* Listing files */
    dirs  = dir(path,"dirs",pattern,1)
    files = dir(path,"files",pattern,1)\dir(path,"files","l"+pattern,1)
    
    real scalar i
    if (atomic)
    {
        for(i=1;i<=length(files);i++)
            unlink(files[i])
    }
    else
    {
        /* We don't want to remove logfiles in tmpdir */
        for(i=1;i<=length(files);i++)
            if ( !regexm(files[i],"do[0-9]+\.log$") | rmlogs)
                unlink(files[i])
    }

    /* Entering each folder */
    for(i=1;i<=length(dirs);i++)
        parallel_recursively_rm(parallelid, dirs[i], 1)

    /* Removing empty folders */
    for(i=1;i<=length(dirs);i++)
        rmdir(dirs[i])


    return
}

end
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_recursively_rm.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_run.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! version 0.14.7.22 22jul2014
*! author: George G. Vega Yon


mata:
{smcl}
*! {marker parallel_run}{bf:function -{it:parallel_run}- in file -{it:parallel_run.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Runs parallel clusters in batch mode.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:parallelid}{col 20}Parallel id.
*!{col 6}{bf:nclusters}{col 20}Number of clusters.
*!{col 6}{bf:paralleldir}{col 20}Dir where the process should be running.
*!{col 6}{bf:timeout}{col 20}Number of seconds to wait until stop the process for no conextion.
*!{col 6}{bf:gateway_fname}{col 20}Name of file that a Cygwin process is listen to will execute from (Windows batch).
*!{col 4}{bf:returns:}
*!{col 6}{it:Number of clusters that stopped with an error.}
*!{dup 78:{c -}}{asis}
real scalar parallel_run(
    string scalar parallelid, 
    |real scalar nclusters, 
    string scalar paralleldir,
    real scalar timeout,
    string scalar gateway_fname
    ) {

    real scalar fh, i
    string scalar tmpdir_i
    
    // Setting default parameters
    if (nclusters == J(1,1,.)) nclusters = strtoreal(st_global("PLL_CLUSTERS"))
    if (paralleldir == J(1,1,"")) paralleldir = st_global("PLL_STATA_PATH")
    
    // Message
    display(sprintf("{hline %g}",c("linesize") > 80?80:c("linesize")))
    display("{result:Parallel Computing with Stata} (by GVY)")
    display("{text:Clusters   :} {result:"+strofreal(nclusters)+"}")
    display("{text:pll_id     :} {result:"+parallelid+"}")
    display("{text:Running at :} {result:"+c("pwd")+"}")
    display("{text:Randtype   :} {result:"+st_local("randtype")+"}")

    string scalar tmpdir
    tmpdir = c("tmpdir") + (regexm(c("tmpdir"),"(/|\\)$") ? "" : "/")
    
    if (c("os") != "Windows") { // MACOS/UNIX
        unlink("__pll"+parallelid+"_shell.sh")
        fh = fopen("__pll"+parallelid+"_shell.sh","w", 1)

        // Writing file
        if (c("os") != "Unix") {
            for(i=1;i<=nclusters;i++) {
                mkdir(tmpdir+"__pll"+parallelid+"_tmpdir"+strofreal(i, "%04.0f"),1) // fput(fh, "mkdir "+c("tmpdir")+"/"+parallelid+strofreal(i,"%04.0f"))
                fput(fh, "export STATATMP="+tmpdir+"__pll"+parallelid+"_tmpdir"+strofreal(i,"%04.0f"))
                fput(fh, paralleldir+`" -e -q do ""'+pwd()+"__pll"+parallelid+"_do"+strofreal(i,"%04.0f")+`".do" &"')
            }
        }
        else {
            for(i=1;i<=nclusters;i++) {
                mkdir(tmpdir+"__pll"+parallelid+"_tmpdir"+strofreal(i, "%04.0f"),1) // fput(fh, "mkdir "+c("tmpdir")+"/__pll"+parallelid+strofreal(i,"%04.0f"))
                fput(fh, "export STATATMP="+tmpdir+"__pll"+parallelid+"_tmpdir"+strofreal(i,"%04.0f"))
                fput(fh, paralleldir+`" -b -q do ""'+pwd()+"__pll"+parallelid+"_do"+strofreal(i,"%04.0f")+`".do" &"')
            }
        }

        fclose(fh)
        
        // stata("shell sh __pll"+parallelid+"shell.sh&")
        stata("winexec sh __pll"+parallelid+"_shell.sh")
    }
    else { // WINDOWS
        if (c("mode")=="batch"){ //Execute commands via Cygwin process
            if (gateway_fname == J(1,1,"")) gateway_fname = st_global("PLL_GATEWAY_FNAME")
            fh = fopen(gateway_fname,"a", 1)
            for(i=1;i<=nclusters;i++) {
                tmpdir_i = tmpdir+"__pll"+parallelid+"_tmpdir"+strofreal(i, "%04.0f")
                mkdir(tmpdir_i,1) // fput(fh, "mkdir "+c("tmpdir")+"/"+parallelid+strofreal(i,"%04.0f"))
                fput(fh, `"export STATATMP=""'+tmpdir_i+`"""')
                fput(fh, paralleldir+`" -e -q do ""'+pwd()+"__pll"+parallelid+"_do"+strofreal(i,"%04.0f")+`".do" &"')
            }
            fclose(fh)
        }
        else{
            unlink("__pll"+parallelid+"_shell.bat")
            fh = fopen("__pll"+parallelid+"_shell.bat","w", 1)
            
            fput(fh, "pushd "+pwd())

            // Writing file
            for(i=1;i<=nclusters;i++) {
                mkdir(tmpdir+"__pll"+parallelid+"_tmpdir"+strofreal(i, "%04.0f"),1)
                fwrite(fh, "start /MIN /HIGH set STATATMP="+tmpdir+"__pll"+parallelid+"_tmpdir"+strofreal(i,"%04.0f")+" ^& ")
                fput(fh, paralleldir+`" /e /q do ""'+"__pll"+parallelid+"_do"+strofreal(i,"%04.0f")+`".do"^&exit"')
            }
            
            fput(fh, "popd")
            fput(fh, "exit")
            
            fclose(fh)
            
            stata("winexec __pll"+parallelid+"_shell.bat")
        }
    }
    
    /* Waits until each process ends */
    return(parallel_finito(parallelid,nclusters,timeout))
}
end


*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_run.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_sandbox.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.4 17apr2014
 *               another parallel process).
mata:
{smcl}
*! {marker parallel_sandbox}{bf:function -{it:parallel_sandbox}- in file -{it:parallel_sandbox.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Set of tools to prevent parallel instances to overlap.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:action}{col 20}Action to be taken.
*!{col 6}{bf:pll_id}{col 20}Parallel process id.
*!{col 6}{bf:result}{col 20}Pointer to list of files that can be removed (without stopping
*!{col 4}{bf:returns:}
*!{col 6}{it:Depends on the action.}
*!{dup 78:{c -}}{asis}
void parallel_sandbox(
    real scalar action,   /* 
        0: Check and create, if error aborts with error ;
        1: Returns a list of files that can be erased 
        2: Delets the respective sandbox file 
        3: Updates the status of a sandbox file
        4:
        5: Retrieves a parallelid as a scalar
        6: Retrieves a list of parallelids that have a sandboxfile
        */
    |string scalar pll_id,
    pointer(scalar) scalar result
    )
{
    /* Definign variables */
    real scalar fh,i;
    string scalar tmpdir
    string colvector sbids, sbfnames;
    
    tmpdir = c("tmpdir")+(c("os") != "Windows" ? "/" : "")

    /* Checks if a parallel instance is currently running with the same pll id name */
    if (action==0)
    {
        /* Checking if the files exist */
        if (fileexists(tmpdir+"__pll"+pll_id+"_sandbox"))
            _error(912,sprintf("-%s- aldready in use. Please change the seed.", pll_id))
        
        /* Creating the new file */
        fh = fopen(tmpdir+"__pll"+pll_id+"_sandbox", "w");
        fput(fh,"pll_id:"+pll_id);
        fput(fh,"date:"+c("current_date")+" "+c("current_time"))
        fput(fh,"usr:"+c("username"))
        fclose(fh);
        
        return
    }
    
    /* Returns a list of files which are not intended to be erased */
    string scalar sbidsi
    if (action==1)
    {
        /* Listing the files that shuldn't be removed */
        sbids = dir(tmpdir,"files","__pll*sandbox",1);
        
        sbfnames = J(0,1,"");
        
        if (length(sbids))
        {
            for(i=1;i<=length(sbids);i++)
            {
                if (regexm(sbids[i],"[_][_]pll(.+)[_]sandbox$"))
                {
                    sbidsi = regexs(1); // regexr(sbids[i], "__pll", ""), "_.*", "");
                    sbfnames = sbfnames\dir(pwd(),"files","__pll"+sbidsi+"*",1)\tmpdir+sbids[i];
                }
            }
        }

        /* Assigning the value */
        (*result) = sbfnames
        
        return
    }
    
    /* Removes the corresponding file to be removed */
    if (action==2)
    {
        unlink(tmpdir+"__pll"+pll_id+"_sandbox")
        return
    }

    /* Updates the status of a parallel instance
    if (action==3)
    {
        fh = fopen("__pll"+pll_id+"_sandbox","rw");
        fseek(fh,2);
        fput(fh,"date:"+c("current_date")+" "+c("current_time"));
        fclose(fh);
        
        return
    } */

    if (action==4)
    {
        /* Listing the folders that shouldn't be removed */
        sbids = dir(tmpdir,"files","__pll*sandbox");
        
        sbfnames = J(0,1,"");

        if (length(sbids))
        {
            for(i=1;i<=length(sbids);i++)
            {
                if (regexm(sbids[i],"[_][_]pll(.+)[_]sandbox$"))
                {
                    sbidsi = regexs(1); // regexr(sbids[i], "__pll", ""), "_.*", "");
                    sbfnames = sbfnames\dir(pwd(),"dirs","__pll"+sbidsi+"*",1)
                }
            }
        }

        /* Assigning the value */
        (*result) = sbfnames

        return

    }
    
    if (action==5)
    {
        real scalar idtaken
        idtaken=1
        while(idtaken)
        {
            pll_id = parallel_randomid(10, "datetime", 1, 1, 1)
            idtaken    = fileexists(tmpdir+"__pll"+pll_id+"sandbox")
        }
        
        /* Securing the pllid */
        parallel_sandbox(0, pll_id)
        st_local("parallelid", pll_id)
        
        return
    }
    
    if (action == 6)
    {
        sbids = dir(tmpdir,"files","__pll*sandbox");
        for(i=1;i<=length(sbids);i++)
            sbids[i] = regexr(regexr(sbids[i],"^__pll",""),"sandbox$","")
            
        /* Assigning the value */
        (*result) = sbfnames
    }
    
}
end

/*
run ado/parallel_clean.mata

mata:
// x=""
parallel_sandbox(2,"cdaozjzrqn")
parallel_sandbox(0,"cdaozjzrqn")
parallel_sandbox(1,"" ,&(x=""))
x
stata("ls")

end
cp __pllcdaozjzrqn_sandbox __pllcdaozjzra_data1.dta, replace
cp __pllcdaozjzrqn_sandbox __pllcdaozjzrqn_data1.dta, replace
ls
mata parallel_clean2("",1)
ls
mata parallel_sandbox(2,"cdaozjzrqn")
mata parallel_clean2("",1)
ls

*/

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_sandbox.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_setclusters.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.3 18mar2014
*! author: George G. Vega Yon

mata:
{smcl}
*! {marker parallel_setclusters}{bf:function -{it:parallel_setclusters}- in file -{it:parallel_setclusters.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Initial cluster setup for parallel.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:ncluster}{col 20}Number of clusters.
*!{col 6}{bf:force}{col 20}Whether to force setting more than 8 clusters.
*!{col 4}{bf:returns:}
*!{col 6}{it:A global PLL_CLUSTERS.}
*!{dup 78:{c -}}{asis}
void parallel_setclusters(real scalar nclusters, |real scalar force) {
        
    // Setting number of clusters
    if (force == J(1,1,.)) force = 0
    if (nclusters <= 8 | (nclusters > 8 & force)) {
        st_global("PLL_CLUSTERS", strofreal(nclusters))
    }
    else _error(912,`"Too many clusters: If you want to set more than 8 clusters you should use the option -force-"')
    display(sprintf("{text:N Clusters}: {result:%g}",nclusters))
}
end
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_setclusters.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_setstatapath.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! vers 0.14.7 22jul2014
*! author: George G. Vega Yon


mata:
{smcl}
*! {marker parallel_setstatapath}{bf:function -{it:parallel_setstatapath}- in file -{it:parallel_setstatapath.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Sets the path where stata exe is installed.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:tatadir}{col 20}If the user wants to set it manually
*!{col 6}{bf:force}{col 20}Avoids path checking.
*!{col 4}{bf:returns:}
*!{col 6}{it:A global PLL_STATA_PATH.}
*!{dup 78:{c -}}{asis}
real scalar parallel_setstatapath(string scalar statadir, | real scalar force) {

    string scalar bit, flv, fname, sys_dir

    // Is it 64bits?
    if (c("osdtl") != "" | c("bit") == 64) bit = "-64"
    else bit = ""
    
    // Building fullpath name
    if (statadir == J(1,1,"") | statadir == "") {
        if (c("os") == "Windows") { // WINDOWS
            if (c("MP")) flv = "MP"
            else if (c("SE")) flv = "SE"
            else if (c("flavor") == "Small") flv = "SM"
            else if (c("flavor") == "IC") flv = ""
        
            /* If the version is less than eleven */
            if (c("stata_version") < 11) fname = "w"+flv+"Stata.exe"
            else fname = "Stata"+flv+bit+".exe"
            
            statadir = c("sysdir_stata") + fname
            
            //might need to convert to cygwin path-name
            if (c("mode")=="batch"){
                if (!force) if (!fileexists(statadir)) return(601)
                statadir = "/cygdrive/"+substr(c("sysdir_stata"),1,1)+"/"+substr(c("sysdir_stata"),4,.) + fname
                force=1
            }
            
        }
        else if (regexm(c("os"), "^MacOS.*")) { // MACOS
        
            if (c("stata_version") < 11 & (c("osdtl") != "" | c("bit") == 64)) bit = "64"
            else bit = ""
        
            if (c("MP")) flv = "Stata"+bit+"MP" 
            else if (c("SE")) flv = "Stata"+bit+"SE"
            else if (c("flavor") == "Small") flv = "smStata"
            else if (c("flavor") == "IC") flv = "Stata"+bit
            
            statadir = c("sysdir_stata")+flv+".app/Contents/MacOS/"+flv
        }
        else { // UNIX
            if (c("MP")) flv = "stata-mp" 
            else if (c("SE")) flv = "stata-se"
            else if (c("flavor") == "Small") flv = "stata-sm"
            else if (c("flavor") == "IC") flv = "stata"
        
            statadir = c("sysdir_stata")+flv
        }
    }

    // Setting PLL_STATA_PATH
    if (force == J(1,1,.)) force = 0
    if (!force) if (!fileexists(statadir)) return(601)
    
    if (!regexm(statadir, `"^["]"')) st_global("PLL_STATA_PATH", `"""'+statadir+`"""')
    else st_global("PLL_STATA_PATH", statadir)
    
    display(sprintf("{text:Stata dir:} {result: %s}" ,statadir))
    return(0)
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_setstatapath.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_write_diagnosis.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! version 0.13.09.30  30sep2013
*! author: George G. Vega Yon

mata:
{smcl}
*! {marker parallel_write_diagnosis}{bf:function -{it:parallel_write_diagnosis}- in file -{it:parallel_write_diagnosis.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Writes a diagnosis to be read by -parallel_finito()-}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:diagnosis}{col 20}Text to be written.
*!{col 6}{bf:fname}{col 20}File where to write the diagnosis.
*!{col 6}{bf:msg}{col 20}Message to include at the end of the file.
*!{col 4}{bf:returns:}
*!{col 6}{it:A file with one or two lines.}
*!{dup 78:{c -}}{asis}
void parallel_write_diagnosis(
    string scalar diagnosis,
    string scalar fname,
    | string scalar msg
) 
{
    real scalar fh
    if (fileexists(fname)) unlink(fname)
    fh = fopen(fname, "w")
    fput(fh, diagnosis)
    fput(fh, msg)
    fclose(fh)
    
}
end
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_write_diagnosis.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:Beginning of file -parallel_write_do.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
*! version 0.14.7.22  22jul2014
* Generates the corresponding dofiles

mata:
{smcl}
*! {marker parallel_write_do}{bf:function -{it:parallel_write_do}- in file -{it:parallel_write_do.mata}-}
*! {back:{it:(previous page)}}
*!{dup 78:{c -}}
*!{col 4}{it:Writes a fully functional do-file to be runned by -parallel_run()-.}
*!{col 4}{bf:parameters:}
*!{col 6}{bf:inputname}{col 20}Name of a do-file or string with a commando to be runned.
*!{col 6}{bf:parallelid}{col 20}Parallel instance ID.
*!{col 6}{bf:ncluster}{col 20}Number of clusters (files).
*!{col 6}{bf:prefix}{col 20}Whether this is a command (prefix != 0) or a do-file.
*!{col 6}{bf:matsave}{col 20}Whether to include or not MATA objects.
*!{col 6}{bf:getmacros}{col 20}Whete to include or not Globals.
*!{col 6}{bf:eed}{col 20}Seed to be used (list)
*!{col 6}{bf:randtype}{col 20}If no seeds provided, type of algorithm used to generate the seeds
*!{col 6}{bf:nodata}{col 20}Wheter to load (1) data or not.
*!{col 6}{bf:folder}{col 20}Folder where the do-file should be running.
*!{col 6}{bf:programs}{col 20}A list of programs to be used within each cluster.
*!{col 6}{bf:processors}{col 20}Number of statamp processors to use in each cluster.
*!{col 4}{bf:returns:}
*!{col 6}{it:As many do-files as clusers used.}
*!{dup 78:{c -}}{asis}
real scalar parallel_write_do(
    string scalar inputname,
    string scalar parallelid,
    | real scalar nclusters,
    real   scalar prefix,
    real   scalar matasave,
    real   scalar getmacros,
    string scalar seed,
    string scalar randtype,
    real   scalar nodata,
    string scalar folder,
    string scalar programs,
    real scalar processors
    )
{
    real vector input_fh, output_fh
    string scalar line, fname
    string scalar memset, maxvarset, matsizeset
    real scalar i
    string colvector seeds

    // Checking optargs
    if (matasave == J(1,1,.)) matasave = 0
    if (prefix == J(1,1,.)) prefix = 1
    if (getmacros == J(1,1,.)) getmacros = 0
    if (nclusters == J(1,1,.)) {
        if (strlen(st_global("PLL_CLUSTERS"))) nclusters = strtoreal(st_global("PLL_CLUSTERS"))
        else {
            errprintf("You haven't set the number of clusters\nPlease set it with -{cmd:parallel setclusters} {it:#}-}\n")
            return(198)
        }
    }
    
    /* Check seeds and seeds length */
    if (seed == J(1,1,""))
    {
        seeds = parallel_randomid(5, randtype, 0, nclusters, 1)
        st_local("pllseeds", invtokens(seeds'))
    }
    else
    {
        st_local("pllseeds", seed)
        seeds = tokens(seed)
        /* Checking seeds length */
        if (length(seeds) > nclusters)
        {
            errprintf("Seeds provided -%g- doesn't match seeds needed -%g-\n", length(seeds), nclusters)
            return(123)
        }
        else if (length(seeds) < nclusters)
        {
            errprintf("Seeds provided -%g- doesn't match seeds needed -%g-\n", length(seeds), nclusters)
            return(122)
        }
    }
    if (nodata == J(1,1,.)) nodata = 0
    if (folder == J(1,1,"")) folder = c("pwd")

    real scalar progsave
    if (programs !="") progsave = 1
    else progsave = 0
    
    /* Checks for the MP version */
    if (!c("MP") & processors != 0 & processors != J(1,1,.)) display("{result:Warning:}{text: processors option ignored...}")
    else if (processors == J(1,1,.) | processors == 0) processors = 1

    real scalar err
    err = 0
    if (progsave)  err = parallel_export_programs(folder+"__pll"+parallelid+"_prog.do", programs, folder+"__pll"+parallelid+"_prog.log")
    if (getmacros) parallel_export_globals(folder+"__pll"+parallelid+"_glob.do")

    if (err)
    {
        errprintf("An error has occurred while exporting -programs-")
        return(err)
    }
    
    for(i=1;i<=nclusters;i++) 
    {
        // Sets dofile
                fname = folder+"__pll"+parallelid+"_do"+strofreal(i,"%04.0f")+".do"
        if (fileexists(fname)) unlink(fname)
        output_fh = fopen(fname, "w", 1)
        
        // Step 1
        fput(output_fh, "capture {")
        fput(output_fh, "clear")
        if (c("MP")) fput(output_fh, "set processors "+strofreal(processors))
        fput(output_fh, `"cd ""'+folder+`"""')
        
        //Copy over the adopath & matalibs order
        //PERSONAL is the most likely to be overwritten and referenced in S_ADO. (could do others)
        fput(output_fh, `"sysdir set PERSONAL ""' + st_global("c(sysdir_personal)") +`"""')
        fput(output_fh, "global S_ADO = `"+`"""'+st_global("S_ADO")+`"""'+"'")
        fput(output_fh, "mata: mata mlib index")
        fput(output_fh, `"mata: mata set matalibs ""'+st_global("c(matalibs)")+`"""')
            
        fput(output_fh, "set seed "+seeds[i])
        
        if (st_global("PLL_INCLUDE_FILE")!=""){
            fput(output_fh, `"include ""'+st_global("PLL_INCLUDE_FILE")+`"""')
        }

        /* Parallel macros to be used by the current user */
        fput(output_fh, `"noi di "{hline 80}""')
        fput(output_fh, `"noi di "Parallel computing with stata (by GVY)""')
        fput(output_fh, `"noi di "{hline 80}""')
        fput(output_fh, sprintf(`"noi di \`"cmd/dofile   : "%s""'"', inputname))
        fput(output_fh, sprintf(`"noi di "pll_id       : %s""',parallelid))
        fput(output_fh, sprintf(`"noi di "pll_instance : %g/%g""',i,nclusters))
        fput(output_fh,         `"noi di "tmpdir       : \`c(tmpdir)'""')
        fput(output_fh,         `"noi di "date-time    : \`c(current_time)' \`c(current_date)'""')
        fput(output_fh,         `"noi di "seed         : \`c(seed)'""')
        fput(output_fh, `"noi di "{hline 80}""')
        fput(output_fh, "local pll_instance "+strofreal(i))
        fput(output_fh, "local pll_id "+parallelid)
        fput(output_fh, "global pll_instance "+strofreal(i))
        fput(output_fh, "global pll_id "+parallelid)
        
        // Data requirements
        if (!nodata)
        {
            if (c("MP") | c("SE")) 
            {
                // Building data limits
                memset     = sprintf("%9.0f",c("memory")/nclusters)
                maxvarset  = sprintf("%g",c("maxvar"))
                matsizeset = sprintf("%g",c("matsize"))

                // Writing data limits
                if (!c("MP")) fput(output_fh, "set memory "+memset+"b")
                fput(output_fh, "set maxvar "+maxvarset)
                fput(output_fh, "set matsize "+matsizeset)
            }
        }
        /* Checking data setting is just fine */
        fput(output_fh, "}")
        fput(output_fh, "local result = _rc")
        fput(output_fh, "if (c(rc)) {")
        fput(output_fh, `"cd ""'+folder+`"""')
        fput(output_fh, `"mata: parallel_write_diagnosis(strofreal(c("rc")),""'+folder+"__pll"+parallelid+"_finito"+strofreal(i,"%04.0f")+`"","while setting memory")"')
        fput(output_fh, "clear")
        fput(output_fh, "exit")
        fput(output_fh, "}")
        
        // Loading programs
        if (progsave)
        {
            fput(output_fh, sprintf("\n/* Loading Programs */"))
            fput(output_fh, "capture {")
            fput(output_fh, "run "+folder+"__pll"+parallelid+"_prog.do")
            /* Checking programs loading is just fine */
            fput(output_fh, "}")
            fput(output_fh, "local result = _rc")
            fput(output_fh, "if (c(rc)) {")
            fput(output_fh, `"cd ""'+folder+`"""')
            fput(output_fh, `"mata: parallel_write_diagnosis(strofreal(c("rc")),""'+folder+"__pll"+parallelid+"_finito"+strofreal(i,"%04.0f")+`"","while loading programs")"')
            fput(output_fh, "clear")
            fput(output_fh, "exit")
            fput(output_fh, "}")
        }
        
        /* Checking for break key 
        fput(output_fh, sprintf("\n/* Checking for break */"))
        fput(output_fh, "mata: parallel_break()") */
        
        // Mata objects loading
        if (matasave)
        {
            fput(output_fh, sprintf("\n/* Loading Mata Objects */"))
            fput(output_fh, "capture {")
            fput(output_fh, `"mata: mata matuse ""'+folder+"__pll"+parallelid+`"_mata.mmat""')
            /* Checking programs loading is just fine */
            fput(output_fh, "}")
            fput(output_fh, "local result = _rc")
            fput(output_fh, "if (c(rc)) {")
            fput(output_fh, `"cd ""'+folder+`"""')
            fput(output_fh, `"mata: parallel_write_diagnosis(strofreal(c("rc")),""'+folder+"__pll"+parallelid+"_finito"+strofreal(i,"%04.0f")+`"","while loading mata objects")"')
            fput(output_fh, "clear")
            fput(output_fh, "exit")
            fput(output_fh, "}")
        }
        
        /* Checking for break key */
        fput(output_fh, sprintf("\n/* Checking for break */"))
        fput(output_fh, "mata: parallel_break()")
        
        // Globals loading
        if (getmacros)
        {
            fput(output_fh, sprintf("\n/* Loading Globals */"))
            fput(output_fh, "capture {")
            fput(output_fh, `"cap run ""'+folder+"__pll"+parallelid+`"_glob.do""')
            /* Checking programs loading is just fine */
            fput(output_fh, "}")
            fput(output_fh, "if (c(rc)) {")
            fput(output_fh, `"  cd ""'+folder+`"""')
            fput(output_fh, `"  mata: parallel_write_diagnosis(strofreal(c("rc")),""'+folder+"__pll"+parallelid+"_finito"+strofreal(i,"%04.0f")+`"","while loading globals")"')
            fput(output_fh, "  clear")
            fput(output_fh, "  exit")
            fput(output_fh, "}")
        }
        
        /* Checking for break key */
        fput(output_fh, sprintf("\n/* Checking for break */"))
        fput(output_fh, "mata: parallel_break()")
                
        // Step 2        
        fput(output_fh, "capture {")
        fput(output_fh, "  noisily {")
        
        // If it is not a command, i.e. a dofile
        if (!nodata) fput(output_fh, `"    use ""'+folder+"__pll"+parallelid+`"_dataset" if _"'+parallelid+"cut == "+strofreal(i))
        
        /* Checking for break key */
        fput(output_fh, sprintf("\n/* Checking for break */"))
        fput(output_fh, "mata: parallel_break()")
        
        if (!prefix) {
            input_fh = fopen(inputname, "r", 1)
            
            while ((line=fget(input_fh))!=J(0,0,"")) fput(output_fh, "    "+line)    
            fclose(input_fh)
        } // if it is a command
        else fput(output_fh, "    "+inputname)
        
        fput(output_fh, "  }")
        fput(output_fh, "}")

        /* Checking programs loading is just fine */
        fput(output_fh, "if (c(rc)) {")
        fput(output_fh, `"  cd ""'+folder+`"""')
        fput(output_fh, `"  mata: parallel_write_diagnosis(strofreal(c("rc")),""'+folder+"__pll"+parallelid+"_finito"+strofreal(i,"%04.0f")+`"","while running the command/dofile")"')
        fput(output_fh, "  clear")
        fput(output_fh, "  exit")
        fput(output_fh, "}")

        fput(output_fh, `"mata: parallel_write_diagnosis(strofreal(c("rc")),""'+folder+"__pll"+parallelid+"_finito"+strofreal(i,"%04.0f")+`"","while executing the command")"')

        if (!nodata) fput(output_fh, `"save ""'+folder+"__pll"+parallelid+"_dta"+strofreal(i,"%04.0f")+`"", replace"')
        
        // Step 3
        fput(output_fh, `"cd ""'+folder+`"""')
        fclose(output_fh)
    }
    return(0)
}
end

*! {smcl}
*! {c TLC}{dup 78:{c -}}{c TRC}
*! {c |} {bf:End of file -parallel_write_do.mata-}{col 83}{c |}
*! {c BLC}{dup 78:{c -}}{c BRC}
